/*
 * Copyright (c) [2020] Huawei Technologies Co.,Ltd.All rights reserved.
 *
 * OpenArkCompiler is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#ifndef MAPLE_RUNTIME_STATS_H
#define MAPLE_RUNTIME_STATS_H

#include <mutex>
#include <memory>
#include <list>
#include <atomic>
#include <algorithm>

#include "mm_config.h"
#include "gc_reason.h"
#include "panic.h"
#include "deps.h"

namespace maplert {
namespace stats {
// Time unit is nanoseconds.
struct SingleGCRecord {
  GCReason reason;
  bool isConcurrentMark;
  bool async;
  uint64_t stw1Time; // First stop-the-world time, or the whole STW if marking is not concurrent.
  uint64_t stw2Time; // Second stop-the-world time, or 0 if marking is not concurrent.
  uint64_t totalGcTime; // Including both marking and sweeping
  size_t objectsCollected;
  size_t bytesCollected;
  size_t bytesSurvived;
  size_t newHeapSize;

  inline uint64_t MaxSTWTime() const {
    if (isConcurrentMark) {
      return std::max(stw1Time, stw2Time);
    } else {
      return stw1Time;
    }
  }

  inline uint64_t TotalSTWTime() const {
    if (isConcurrentMark) {
      return stw1Time + stw2Time;
    } else {
      return stw1Time;
    }
  }
};

// Public-visible statistics generated by triggered GCs.
// Time unit is nanoseconds.
class GCStats {
 public:
  // GC record handling.
  // Call this at the beginning of GC to initialize the curRec field.
  void BeginGCRecord();
  void CommitGCRecord();
  SingleGCRecord &CurrentGCRecord() {
    __MRT_ASSERT(curRec != nullptr, "curRec is nullptr.  Call BeginGCRecord() first!");
    return *curRec;
  }

  // Writers.  Collect statistics from different place of the runtime.
  void OnCollectorInit();
  void OnAllocAnomaly();
  void OnFreeObject(size_t size) const;
  void OnRCAnomaly();

  // Readers.
  uint64_t MaxSTWNanos() const;
  size_t NumGCTriggered() const;
  size_t AverageMemoryLeak() const;
  // returns the bytes claimed by the tracing GC
  size_t TotalMemoryLeak() const;
  double MemoryUtilization() const;
  size_t NumAllocAnomalies() const;
  size_t NumRCAnomalies() const;
  void ResetMaxSTWNanos();
  void ResetNumGCTriggered();
  void ResetMemoryLeak();
  void ResetMemoryUtility();
  void ResetNumAllocAnomalies();
  void ResetNumRCAnomalies();

  size_t CurAllocBytes() const;
  size_t CurAllocatorCapacity() const;
  size_t CurSpaceCapacity() const;
  size_t CurGCThreshold() const;
  void InitialGCThreshold(const bool isSystem);
  void InitialGCProcessName();

  GCStats();
  ~GCStats() = default;

 private:
  std::atomic<size_t> numGcTriggered;
  // Total bytes collected since last reset
  // this field is incremented inside OnGCFinished; so we do not need to increment
  // on every object release.
  size_t totalBytesCollected;
  // The number of gc times since last reset
  size_t recentGcCount;
  // The max bytes collected by gc since last reset
  size_t maxBytesCollected;
  std::atomic<uint64_t> maxStopTheWorldTime;
  // The number of allocation requests that failed due to OOM. Reset on every getMemAlloc
  std::atomic<size_t> numAllocAnomalies;
  std::atomic<size_t> numRcAnomalies;
  std::atomic<size_t> currentGcThreshold;

  // Current GC record. Need to be written by many different classes, so we put it here.
  std::unique_ptr<SingleGCRecord> curRec;
  float waterLevelLow;
  float waterLevel;
  std::string processName;
  void UpdateStatistics(const std::unique_ptr<SingleGCRecord> &rec);
  void Dump(const std::unique_ptr<SingleGCRecord> &rec);
  // Add the GC record.
  // GC thread should use Begin/Current/CommitGCRecord instead.
  void OnGCFinished(const std::unique_ptr<SingleGCRecord> &rec);
};

extern ImmortalWrapper<GCStats> gcStats;
} // namespace stats
} // namespace maplert

#endif
